package Common;

import java.util.Vector;

/**
 *
 * @author Seth Wessitsh <seth@wessitsh.com>
 */
public class MathUtils {
	/**
	 * Used for Debugging
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println(cosDist(
			new Double[] {0.27741935483870966,0.0625},
			new Double[] {0.4435483870967742,0.5181451612903226}
		)/Math.PI);
		System.out.println(cosDist(
			new Double[] {1.0,0.0},
			new Double[] {0.0,1.0}
		));

		Double[] d = new Double[] {1.0,1.0,1.0,3.0,5.0,7.0,9.0,11.0,13.0,15.0,15.0,15.0};
		System.out.println("stdDev: " + stdDev( d ) );
		System.out.println("mean: " +  mean( d ) );
		System.out.println("cv: " +  cv( d ) );

		d = new Double[] {
			0.0, 1.0, 1.0
		};
		System.out.println("stdDev: " + stdDev( d ) );
		System.out.println("mean: " +  mean( d ) );
		System.out.println("cv: " +  cv( d ) );
	}

	/**
	 * Calculates the angle between two vectors <code>v1</code> and <code>v2</code>.
	 * In other words, the cosine distance between two vectors.
	 *
	 * The angle is calculated as follows:
	 *
	 *              dot(v1,v2)
	 *    arccos( -------------- )
	 *            mag(v1)mag(v2)
	 *
	 * @param v1 the first vector
	 * @param v2 the second vector
	 * @return the angle between two vectors <code>v1</code> and <code>v2</code>
	 *          the minimum value is 0.0 and the maximum value is PI
	 */
	public static double cosDist(Double[] v1, Double[] v2) {
		/**
		 * Slight variations in dot and mag*mag can cause d to be just
		 * barely greater than one so we must account for this
		 */
		double d = dot(v1,v2)/(mag(v1)*mag(v2));
		return Math.acos(d > 1 ? 1 : d);
	}

	/**
	 * @see cosDist(Double[] v1, Double[] v2)
	 */
	public static double cosDist(Vector<Double> v1, Vector<Double> v2) {
		double d = dot(v1,v2)/(mag(v1)*mag(v2));
		return Math.acos(d > 1 ? 1 : d);
	}

	/**
	 * Calculates the Euclidean distance between vectors <code>v1</code> and <code>v2</code>
	 *
	 * The Euclidean distance is calculated as follows:
	 * 
	 *               l
	 * SquareRoot(  Sum (v1[i]-v2[i])*(v1[i]-v2[i])  )
	 *              i=0
	 *
	 * Where l is the length of the smaller of the two vectors.  Technically the distance
	 * shouldn't be defined for vectors with different lengths.  I'm following the
	 * garbage in garbage out rule here.
	 *
	 * @param v1
	 * @param v2
	 * @return the Euclidean distance between v1 and v2
	 */
	public static double eucDist(Double[] v1, Double[] v2) {
		double sum = 0;
		int loops = Math.min(v1.length, v2.length);

		for (int i=0; i < loops; i++) {
			double diff = v1[i]-v2[i];
			sum += diff*diff;
		}

		return Math.sqrt(sum);
	}

	/**
	 * @see #eucDist(java.lang.Double[], java.lang.Double[])
	 * 
	 * @param v1
	 * @param v2
	 * @return the Euclidean distance between v1 and v2
	 */
	public static double eucDist(Vector<Double> v1, Vector<Double> v2) {
		double sum = 0;
		int loops = Math.min(v1.size(), v2.size());

		for (int i=0; i < loops; i++) {
			double diff = v1.get(i)-v2.get(i);
			sum += diff*diff;
		}

		return Math.sqrt(sum);

	}

	/**
	 * Calculates the magnitude/norm of vector <code>v</code>
	 *
	 * The norm is calculated as follows:
	 *              l
	 * Math.sqrt(  Sum v[i]*v[i]  )
	 *             i=0
	 *
	 * Where l is the length (number of components) of v
	 *
	 * @param v the vector to calculate the magnitude/norm of
	 * @return the magnitude/norm of vector <code>v</code>
	 */
	public static double mag(Double[] v) {
		double mag = 0;
		for (Double d : v) mag += d*d;
		return Math.sqrt(mag);
	}

	/**
	 * @see mag(Double[] v)
	 *
	 * @return the magnitude/norm of vector <code>v</code>
	 */
	public static double mag(Vector<Double> v) {
		double mag = 0;
		for (Double d : v) mag += d*d;
		return Math.sqrt(mag);
	}

	/**
	 * Calculate the dot product of two vectors <code>v1</code> and <code>v2</code>
	 *
	 * The dot produt is calculated as follows:
	 *  l
	 * Sum v1[i]*v2[i]
	 * i=0
	 *
	 * Where l is the smaller of the lengths of the vectors. Technically the product
	 * shouldn't be defined for vectors with different lengths.  I'm following the
	 * garbage in garbage out rule here.
	 *
	 * @param v1
	 * @param v2
	 * @return the dot product of v1 and v2
	 */
	public static double dot(Double[] v1, Double[] v2) {
		double prod = 0;
		int loops = Math.min(v1.length, v2.length);

		for (int i=0; i < loops; i++) {
			prod += (v1[i]*v2[i]);
		}

		return prod;
	}

	/**
	 * @see dot(Double[] v1, Double[] v2)
	 * 
	 * @return the dot product of v1 and v2
	 */
	public static double dot(Vector<Double> v1, Vector<Double> v2) {
		double prod = 0;
		int loops = Math.min(v1.size(), v2.size());

		for (int i=0; i < loops; i++) {
			prod += (v1.get(i)*v2.get(i));
		}

		return prod;
	}

	/**
	 * The mean of vector v
	 *   1  N
	 *   - Sum v[i]
	 *   N i=0
	 * Where N is the length of vector v
	 *
	 * @param v
	 * @return the mean of vector v
	 */
	public static double mean(Double[] v) {
		double sum = 0;
		for (Double d : v) sum += d;
		return sum/v.length;
	}

	/**
	 * @see #mag(java.lang.Double[])
	 *
	 * @param v
	 * @return the mean of vector v
	 */
	public static double mean(Vector<Double> v) {
		double sum = 0;
		for (Double d : v) sum += d;
		return sum/v.size();
	}

	/**
	 * The standard deviation of vector v
	 * 
	 *              1  N
	 * SquareRoot(  - Sum (v[i] - m)*(v[i] - m)  )
	 *              N i=0
	 * Where N is the length of v and m is the mean of v
	 * 
	 * @param v
	 * @return the standard deviation of vector v
	 */
	public static double stdDev(Double[] v) {
		double m = mean(v);
		double sum = 0;

		for (Double d : v) sum += ((d-m)*(d-m));

		return Math.sqrt(sum/v.length);
	}

	/**
	 * @see #stdDev(java.lang.Double[])
	 * 
	 * @param v
	 * @return the standard deviation of vector v
	 */
	public static double stdDev(Vector<Double> v) {
		double m = mean(v);
		double sum = 0;

		for (Double d : v) sum += ((d-m)*(d-m));

		return Math.sqrt(sum/v.size());
	}

	/**
	 * The coefficient of variance for vector v
	 *
	 * stdDev(v) / mean(v)
	 *
	 * @param v
	 * @return the coefficient of variance for vector v (stdDev(v) / mean(v))
	 */
	public static double cv(Double[] v) {
		return stdDev(v)/mean(v);
	}

	/**
	 * @see #cv(java.lang.Double[])
	 * 
	 * @param v
	 * @return the coefficient of variance for vector v (stdDev(v) / mean(v))
	 */
	public static double cv(Vector<Double> v) {
		return stdDev(v)/mean(v);
	}
}
